﻿using Baby.Shop.Application.Contracts.Common.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Baby.Shop.Application.Contracts.Common.Helper.Linq
{
    /// <summary>
    /// 表达式扩展
    /// </summary>
    /// <typeparam name="T">泛型</typeparam>
    public static class ExpressionExtension<T> where T : class, new()
    {
        /// <summary>
        /// 表达式动态拼接
        /// </summary>
        public static Expression<Func<T, bool>> ExpressionSplice(List<QueryEntity> entities)
        {
            if (entities.Count < 1)
            {
                return ex => true;
            }
            var expression_first = CreateExpressionDelegate(entities[0]);
            foreach (var entity in entities.Skip(1))
            {
                var expression = CreateExpressionDelegate(entity);
                InvocationExpression invocation = Expression.Invoke(expression_first, expression.Parameters.Cast<Expression>());
                BinaryExpression binary;
                // 逻辑运算符判断
                if (entity.LogicalOperator.ToUpper().Equals("OR"))
                {
                    binary = Expression.Or(expression.Body, invocation);
                }
                else
                {
                    binary = Expression.And(expression.Body, invocation);
                }
                expression_first = Expression.Lambda<Func<T, bool>>(binary, expression.Parameters);
            }
            return expression_first;
        }

        /// <summary>
        /// Expression<TDelegate>
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        private static Expression<Func<T, bool>> CreateExpressionDelegate(QueryEntity entity)
        {
            ParameterExpression param = Expression.Parameter(typeof(T));
            Expression key = param;
            var entityKey = entity.Field.Trim();
            if (entityKey.Contains('.'))
            {
                var tableNameAndField = entityKey.Split('.');
                key = Expression.Property(key, tableNameAndField[0].ToString());
                key = Expression.Property(key, tableNameAndField[1].ToString());
            }
            else
            {
                key = Expression.Property(key, entityKey);
            }
            Expression value;
            value = Expression.Constant(ParseType(entity));
            Expression body = CreateExpression(key, value, entity.Method);
            var lambda = Expression.Lambda<Func<T, bool>>(body, param);
            return lambda;
        }

        /// <summary>
        /// 属性类型转换
        /// </summary>
        /// <param name="entity">查询实体</param>
        /// <returns></returns>
        private static object ParseType(QueryEntity entity)
        {
            try
            {
                if (entity is null)
                    throw new ArgumentException("属性实体不能为空");
                if (string.IsNullOrEmpty(entity.Field))
                    throw new ArgumentException("属性字段不能为空");
                PropertyInfo? property;
                Type type = typeof(T) ?? typeof(object);
                // 包含'.'，说明是子类的字段
                if (entity.Field.Contains('.'))
                {
                    var tableNameAndField = entity.Field.Split('.');
                    if (string.IsNullOrEmpty(tableNameAndField[0]) || string.IsNullOrEmpty(tableNameAndField[1]))
                        throw new ArgumentException("属性字段不能为空");
                    property = type.GetProperty(tableNameAndField[0], BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                    property = property?.PropertyType.GetProperty(tableNameAndField[1], BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                }
                else
                {
                    property = type.GetProperty(entity.Field, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                }
                if (property is null)
                    throw new ArgumentException("字段为空类型");
                Type t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
                if (entity.Method == OperatorEnum.In)
                {
                    //如果条件是in类型就转为List《T》
                    //Type elementType = key.Type;
                    Type listType = typeof(List<>).MakeGenericType(t);
                    return Convert.ChangeType(entity.Value, listType);
                }
                return Convert.ChangeType(entity.Value, t);
            }
            catch (Exception)
            {
                throw new ArgumentException("字段类型转换失败：字段名错误或值类型不正确");
            }
        }

        /// <summary>
        /// 创建 Expression
        /// </summary>
        private static Expression CreateExpression(Expression left, Expression value, OperatorEnum entityOperator)
        {
            switch (entityOperator)
            {
                case OperatorEnum.Equals: return Expression.Equal(left, Expression.Convert(value, left.Type)); ;
                case OperatorEnum.NotEqual: return Expression.NotEqual(left, Expression.Convert(value, left.Type)); ;
                case OperatorEnum.Contains:
                    MethodInfo? methodContains = typeof(string)?.GetMethod("Contains", new Type[] { typeof(string) });
                    if (methodContains == null)
                        throw new ArgumentException("Contains条件中不存在方法Contains");
                    return Expression.Call(left, methodContains, value); ;
                case OperatorEnum.StartsWith:
                    MethodInfo? methodStartsWith = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
                    if (methodStartsWith == null)
                        throw new ArgumentException("StartsWith条件中不存在方法StartsWith");
                    return Expression.Call(left, methodStartsWith, value); ;
                case OperatorEnum.EndsWith:
                    MethodInfo? methodEndsWith = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) });
                    if (methodEndsWith == null)
                        throw new ArgumentException("EndsWith条件中不存在方法EndsWith");
                    return Expression.Call(left, methodEndsWith, value);
                case OperatorEnum.Greater: return Expression.GreaterThan(left, Expression.Convert(value, left.Type)); ;
                case OperatorEnum.GreaterEqual: return Expression.GreaterThanOrEqual(left, Expression.Convert(value, left.Type));
                case OperatorEnum.Less: return Expression.LessThan(left, Expression.Convert(value, left.Type)); ;
                case OperatorEnum.LessEqual: return Expression.LessThanOrEqual(left, Expression.Convert(value, left.Type));
                case OperatorEnum.In:
                    Type elementType = left.Type;
                    Type listType = typeof(List<>).MakeGenericType(elementType);
                    MethodInfo? method = listType?.GetMethod("Contains", new Type[] { left.Type });
                    if (method == null)
                        throw new ArgumentException("In条件中不存在方法Contains");
                    return Expression.Call(value, method, left);
                default:
                    return Expression.Equal(left, Expression.Convert(value, left.Type));

            };

        }

    }
}
